
using System;

namespace raycsharp
{

	public class Raycasting
	{
		private Scene scene { get; set; }
		private Camera camera { get; set; }
		
		public Raycasting (Scene _scene, Camera _camera) {
			scene = _scene;
			camera = _camera;
		}
		
		public Color[,] trace() {
			uint width = camera.grid.width;
			uint height = camera.grid.height;
			Color[,] image = new Color[width,height];
			uint x;
			for(x = 0; x < width; x++) {
				for(uint y = 0; y < height; y++) {
					Ray ray = camera.generate_ray(x,y);
					
					IntersectionResult ir = scene.nearest_object(ray);
					if(!ir.status) {
						image[x,y] = new Color();
					}
					else {
						image[x,y] = calculate_color(ir.obj,ray,ir.intersection_point);
					}
				}
			}
			
			
			return image;
		}
		public Color calculate_color(SceneObject _object, Ray _ray, Vector _point) {
			Color result_color = new Color();
			
			Color oambient  = _object.ambient;
			Color odiffuse  = _object.diffuse;
			Color ospecular = _object.specular;
			
			Vector normal = _object.get_normal(_point);
			
			double ambient = 0.0; //Magic :D
						
			foreach(Light light in scene.lights) {
				Color lcolor  = light.color;	
				
				Vector L = (light.point.sub_vect(_point )).normalize();
								
				double diffuse  = calculate_diffuse (L, normal);
				double specular = calculate_specular(L, normal, _ray);
					
				result_color = result_color + (specular*lcolor*ospecular +
				                               diffuse*lcolor*odiffuse   +
				                               ambient*lcolor*oambient);
			}
			//Phong
			
			
			
			return result_color;
		}
		
		private Vector calculate_reflection(Vector _incident, Vector normal) {
			return (normal.scalar_product(normal.dot_product(_incident)*2.0)).sub_vect(_incident);
		}

		
		public double calculate_diffuse(Vector L, Vector _normal) {
			return Math.Max(0.0,L.dot_product(_normal));
		}
		
		public double calculate_specular(Vector L, Vector _normal, Ray _ray) {
			Vector reflection = calculate_reflection(L,_normal);
			return Math.Pow(Math.Max(0.0,((_ray.direction.scalar_product(-1.0)).dot_product(reflection))),25);
			
		}
		
		
	}
}
